Multi-Class Image Classification Model for Fruits and Vegetables Image Recognition Using TensorFlow Take 6¶

David Lowe¶

April 22, 2022¶

SUMMARY: This project aims to construct a predictive model using a TensorFlow convolutional neural network (CNN) and document the end-to-end steps using a template. The Fruits and Vegetables Image Recognition dataset is a multi-class classification situation where we attempt to predict one of several (more than two) possible outcomes.

INTRODUCTION: The dataset owner collected over 4,300 pieces of fruit and vegetable images and created a dataset that includes 36 classes. The idea was to build an application that recognizes the food items from the captured photo and provides different recipes that can be made using the food items.

ANALYSIS: The InceptionV3 model's performance achieved an accuracy score of 93.45% after 20 epochs using a separate validation dataset. After tuning the learning rate, we improved the accuracy rate to 96.87% using the same validation dataset. When we applied the model to the test dataset, the model achieved an accuracy score of 95.54%.

CONCLUSION: In this iteration, the TensorFlow InceptionV3 CNN model appeared suitable for modeling this dataset.

Dataset ML Model: Multi-Class classification with numerical features

Dataset Used: Kritik Seth, "Fruits and Vegetables Image Recognition Dataset," Kaggle 2020

Dataset Reference: https://www.kaggle.com/datasets/kritikseth/fruit-and-vegetable-image-recognition

One source of potential performance benchmarks: https://www.kaggle.com/datasets/kritikseth/fruit-and-vegetable-image-recognition/code

Task 1 - Prepare Environment¶

In [1]:
# # Install the packages to support accessing environment variable and SQL databases
# !pip install python-dotenv PyMySQL boto3
In [2]:
# Retrieve CPU information from the system
ncpu = !nproc
print("The number of available CPUs is:", ncpu[0])
The number of available CPUs is: 2
In [3]:
# Retrieve memory configuration information
from psutil import virtual_memory
ram_gb = virtual_memory().total / 1e9
print('Your runtime has {:.1f} gigabytes of available RAM\n'.format(ram_gb))
Your runtime has 13.6 gigabytes of available RAM

In [4]:
# Retrieve GPU configuration information
gpu_info = !nvidia-smi
gpu_info = '\n'.join(gpu_info)
print(gpu_info)
Mon Apr 11 13:13:54 2022       
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 460.32.03    Driver Version: 460.32.03    CUDA Version: 11.2     |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|                               |                      |               MIG M. |
|===============================+======================+======================|
|   0  Tesla V100-SXM2...  Off  | 00000000:00:04.0 Off |                    0 |
| N/A   32C    P0    23W / 300W |      0MiB / 16160MiB |      0%      Default |
|                               |                      |                  N/A |
+-------------------------------+----------------------+----------------------+
                                                                               
+-----------------------------------------------------------------------------+
| Processes:                                                                  |
|  GPU   GI   CI        PID   Type   Process name                  GPU Memory |
|        ID   ID                                                   Usage      |
|=============================================================================|
|  No running processes found                                                 |
+-----------------------------------------------------------------------------+
In [5]:
# # Mount Google Drive locally for loading the dotenv files
# from dotenv import load_dotenv
# from google.colab import drive
# drive.mount('/content/gdrive')
# gdrivePrefix = '/content/gdrive/My Drive/Colab_Downloads/'
# env_path = '/content/gdrive/My Drive/Colab Notebooks/'
# dotenv_path = env_path + "python_script.env"
# load_dotenv(dotenv_path=dotenv_path)

1.a) Load libraries and modules¶

In [6]:
# Set the random seed number for reproducible results
RNG_SEED = 888
In [7]:
import random
random.seed(RNG_SEED)
import numpy as np
np.random.seed(RNG_SEED)
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
import os
import sys
import math
# import boto3
import zipfile
from datetime import datetime
from sklearn.metrics import classification_report
from sklearn.metrics import confusion_matrix
from sklearn.metrics import accuracy_score

import tensorflow as tf
tf.random.set_seed(RNG_SEED)
from tensorflow import keras
from tensorflow.keras.callbacks import ReduceLROnPlateau
from tensorflow.keras.preprocessing.image import ImageDataGenerator

1.b) Set up the controlling parameters and functions¶

In [8]:
# Begin the timer for the script processing
START_TIME_SCRIPT = datetime.now()
In [9]:
# Set up the number of CPU cores available for multi-thread processing
N_JOBS = 1

# Set up the flag to stop sending progress emails (setting to True will send status emails!)
NOTIFY_STATUS = False

# Set the percentage sizes for splitting the dataset
TEST_SET_RATIO = 0.2
VAL_SET_RATIO = 0.2

# Set the number of folds for cross validation
N_FOLDS = 5
N_ITERATIONS = 1

# Set various default modeling parameters
DEFAULT_LOSS = 'categorical_crossentropy'
DEFAULT_METRICS = ['accuracy']
INITIAL_LR = 0.0001
DEFAULT_OPTIMIZER = tf.keras.optimizers.Adam(learning_rate=INITIAL_LR)
CLASSIFIER_ACTIVATION = 'softmax'
MAX_EPOCHS = 20
BATCH_SIZE = 16
NUM_CLASSES = 36
# CLASS_LABELS = []
# CLASS_NAMES = []
# RAW_IMAGE_SIZE = (250, 250)
TARGET_IMAGE_SIZE = (299, 299)
INPUT_IMAGE_SHAPE = (TARGET_IMAGE_SIZE[0], TARGET_IMAGE_SIZE[1], 3)

# Define the labels to use for graphing the data
TRAIN_METRIC = "accuracy"
VALIDATION_METRIC = "val_accuracy"
TRAIN_LOSS = "loss"
VALIDATION_LOSS = "val_loss"

# Define the directory locations and file names
STAGING_DIR = 'staging/'
TRAIN_DIR = 'staging/train/'
VALID_DIR = 'staging/validation/'
TEST_DIR = 'staging/test/'
TRAIN_DATASET = 'archive.zip'
# VALID_DATASET = ''
# TEST_DATASET = ''
# TRAIN_LABELS = ''
# VALID_LABELS = ''
# TEST_LABELS = ''
# OUTPUT_DIR = 'staging/'
# SAMPLE_SUBMISSION_CSV = 'sample_submission.csv'
# FINAL_SUBMISSION_CSV = 'submission.csv'

# Check the number of GPUs accessible through TensorFlow
print('Num GPUs Available:', len(tf.config.list_physical_devices('GPU')))

# Print out the TensorFlow version for confirmation
print('TensorFlow version:', tf.__version__)
Num GPUs Available: 1
TensorFlow version: 2.8.0
In [10]:
# Set up the email notification function
def status_notify(msg_text):
    access_key = os.environ.get('SNS_ACCESS_KEY')
    secret_key = os.environ.get('SNS_SECRET_KEY')
    aws_region = os.environ.get('SNS_AWS_REGION')
    topic_arn = os.environ.get('SNS_TOPIC_ARN')
    if (access_key is None) or (secret_key is None) or (aws_region is None):
        sys.exit("Incomplete notification setup info. Script Processing Aborted!!!")
    sns = boto3.client('sns', aws_access_key_id=access_key, aws_secret_access_key=secret_key, region_name=aws_region)
    response = sns.publish(TopicArn=topic_arn, Message=msg_text)
    if response['ResponseMetadata']['HTTPStatusCode'] != 200 :
        print('Status notification not OK with HTTP status code:', response['ResponseMetadata']['HTTPStatusCode'])
In [11]:
if NOTIFY_STATUS: status_notify('(TensorFlow Multi-Class) Task 1 - Prepare Environment has begun on ' + datetime.now().strftime('%A %B %d, %Y %I:%M:%S %p'))
In [12]:
if NOTIFY_STATUS: status_notify('(TensorFlow Multi-Class) Task 1 - Prepare Environment completed on ' + datetime.now().strftime('%A %B %d, %Y %I:%M:%S %p'))

Task 2 - Load and Prepare Images¶

In [13]:
if NOTIFY_STATUS: status_notify('(TensorFlow Multi-Class) Task 2 - Load and Prepare Images has begun on ' + datetime.now().strftime('%A %B %d, %Y %I:%M:%S %p'))
In [14]:
# Clean up the old files and download directories before receiving new ones
!rm -rf staging/
# !rm archive.zip
!mkdir staging/
In [15]:
if not os.path.exists(TRAIN_DATASET):
    !wget https://dainesanalytics.com/datasets/kaggle-kritikseth-fruit-vegetable-image/archive.zip
--2022-04-11 13:14:01--  https://dainesanalytics.com/datasets/kaggle-kritikseth-fruit-vegetable-image/archive.zip
Resolving dainesanalytics.com (dainesanalytics.com)... 18.64.115.12, 18.64.115.127, 18.64.115.65, ...
Connecting to dainesanalytics.com (dainesanalytics.com)|18.64.115.12|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 2130757290 (2.0G) [application/zip]
Saving to: ‘archive.zip’

archive.zip         100%[===================>]   1.98G   139MB/s    in 18s     

2022-04-11 13:14:19 (112 MB/s) - ‘archive.zip’ saved [2130757290/2130757290]

In [16]:
zip_ref = zipfile.ZipFile(TRAIN_DATASET, 'r')
zip_ref.extractall(STAGING_DIR)
zip_ref.close()
In [17]:
CLASS_LABELS = os.listdir(TRAIN_DIR)
print(CLASS_LABELS)
['onion', 'tomato', 'turnip', 'apple', 'pomegranate', 'sweetcorn', 'ginger', 'peas', 'lettuce', 'garlic', 'watermelon', 'potato', 'paprika', 'eggplant', 'carrot', 'bell pepper', 'sweetpotato', 'jalepeno', 'orange', 'pineapple', 'soy beans', 'lemon', 'kiwi', 'chilli pepper', 'cucumber', 'raddish', 'cabbage', 'spinach', 'mango', 'pear', 'capsicum', 'beetroot', 'grapes', 'corn', 'cauliflower', 'banana']
In [18]:
# Brief listing of training image files for each class
for c_label in CLASS_LABELS:
    training_class_dir = os.path.join(TRAIN_DIR, c_label)
    training_class_files = os.listdir(training_class_dir)
    print('Number of training images for', c_label, ':', len(os.listdir(training_class_dir)))
    print('Training samples for', c_label, ':', training_class_files[:5],'\n')
Number of training images for onion : 94
Training samples for onion : ['Image_84.jpg', 'Image_1.jpg', 'Image_40.jpg', 'Image_3.jpg', 'Image_94.png'] 

Number of training images for tomato : 92
Training samples for tomato : ['Image_84.jpg', 'Image_1.jpg', 'Image_40.jpg', 'Image_3.jpg', 'Image_80.png'] 

Number of training images for turnip : 98
Training samples for turnip : ['Image_84.jpg', 'Image_64.jpg', 'Image_1.jpg', 'Image_40.jpg', 'Image_3.jpg'] 

Number of training images for apple : 68
Training samples for apple : ['Image_64.jpg', 'Image_1.jpg', 'Image_40.jpg', 'Image_3.jpg', 'Image_87.jpg'] 

Number of training images for pomegranate : 79
Training samples for pomegranate : ['Image_64.jpg', 'Image_1.jpg', 'Image_40.jpg', 'Image_3.jpg', 'Image_80.png'] 

Number of training images for sweetcorn : 91
Training samples for sweetcorn : ['Image_84.jpg', 'Image_1.jpg', 'Image_40.jpg', 'Image_3.jpg', 'Image_76.jpg'] 

Number of training images for ginger : 68
Training samples for ginger : ['Image_64.jpg', 'Image_1.jpg', 'Image_40.jpg', 'Image_3.jpg', 'Image_7.jpg'] 

Number of training images for peas : 100
Training samples for peas : ['Image_84.jpg', 'Image_64.jpg', 'Image_1.jpg', 'Image_40.jpg', 'Image_3.jpg'] 

Number of training images for lettuce : 97
Training samples for lettuce : ['Image_84.jpg', 'Image_64.jpg', 'Image_1.jpg', 'Image_40.jpg', 'Image_3.jpg'] 

Number of training images for garlic : 92
Training samples for garlic : ['Image_84.jpg', 'Image_64.jpg', 'Image_1.jpg', 'Image_40.jpg', 'Image_3.jpg'] 

Number of training images for watermelon : 84
Training samples for watermelon : ['Image_84.jpg', 'Image_1.jpg', 'Image_40.jpg', 'Image_3.jpg', 'Image_89.png'] 

Number of training images for potato : 77
Training samples for potato : ['Image_64.jpg', 'Image_1.jpg', 'Image_3.jpg', 'Image_80.png', 'Image_76.jpg'] 

Number of training images for paprika : 83
Training samples for paprika : ['Image_14.png', 'Image_85.jpeg', 'Image_84.jpg', 'Image_64.jpg', 'Image_1.jpg'] 

Number of training images for eggplant : 84
Training samples for eggplant : ['Image_84.jpg', 'Image_64.jpg', 'Image_1.jpg', 'Image_3.jpg', 'Image_87.jpg'] 

Number of training images for carrot : 82
Training samples for carrot : ['Image_84.jpg', 'Image_64.jpg', 'Image_1.jpg', 'Image_40.jpg', 'Image_3.jpg'] 

Number of training images for bell pepper : 90
Training samples for bell pepper : ['Image_84.jpg', 'Image_64.jpg', 'Image_1.jpg', 'Image_40.jpg', 'Image_41.png'] 

Number of training images for sweetpotato : 69
Training samples for sweetpotato : ['Image_84.jpg', 'Image_64.jpg', 'Image_40.jpg', 'Image_3.jpg', 'Image_76.jpg'] 

Number of training images for jalepeno : 88
Training samples for jalepeno : ['Image_84.jpg', 'Image_64.jpg', 'Image_40.jpg', 'Image_3.jpg', 'Image_87.jpg'] 

Number of training images for orange : 69
Training samples for orange : ['Image_40.jpg', 'Image_3.jpg', 'Image_76.jpg', 'Image_87.jpg', 'Image_7.jpg'] 

Number of training images for pineapple : 99
Training samples for pineapple : ['Image_84.jpg', 'Image_64.jpg', 'Image_1.jpg', 'Image_40.jpg', 'Image_3.jpg'] 

Number of training images for soy beans : 97
Training samples for soy beans : ['Image_84.jpg', 'Image_64.jpg', 'Image_1.jpg', 'Image_40.jpg', 'Image_3.jpg'] 

Number of training images for lemon : 82
Training samples for lemon : ['Image_84.jpg', 'Image_3.jpg', 'Image_8.png', 'Image_7.jpg', 'Image_31.jpg'] 

Number of training images for kiwi : 88
Training samples for kiwi : ['Image_84.jpg', 'Image_1.jpg', 'Image_40.jpg', 'Image_3.jpg', 'Image_76.jpg'] 

Number of training images for chilli pepper : 87
Training samples for chilli pepper : ['Image_84.jpg', 'Image_64.jpg', 'Image_1.jpg', 'Image_40.jpg', 'Image_3.jpg'] 

Number of training images for cucumber : 94
Training samples for cucumber : ['Image_84.jpg', 'Image_64.jpg', 'Image_1.jpg', 'Image_40.jpg', 'Image_3.jpg'] 

Number of training images for raddish : 81
Training samples for raddish : ['Image_84.jpg', 'Image_64.jpg', 'Image_1.jpg', 'Image_40.jpg', 'Image_3.jpg'] 

Number of training images for cabbage : 92
Training samples for cabbage : ['Image_1.jpg', 'Image_26.JPG', 'Image_3.jpg', 'Image_76.jpg', 'Image_87.jpg'] 

Number of training images for spinach : 97
Training samples for spinach : ['Image_84.jpg', 'Image_1.jpg', 'Image_40.jpg', 'Image_3.jpg', 'Image_80.png'] 

Number of training images for mango : 86
Training samples for mango : ['Image_64.jpg', 'Image_1.jpg', 'Image_3.jpg', 'Image_87.jpg', 'Image_7.jpg'] 

Number of training images for pear : 89
Training samples for pear : ['Image_64.jpg', 'Image_1.jpg', 'Image_40.jpg', 'Image_3.jpg', 'Image_76.jpg'] 

Number of training images for capsicum : 89
Training samples for capsicum : ['Image_96.JPG', 'Image_84.jpg', 'Image_64.jpg', 'Image_1.jpg', 'Image_40.jpg'] 

Number of training images for beetroot : 88
Training samples for beetroot : ['Image_64.jpg', 'Image_1.jpg', 'Image_40.jpg', 'Image_3.jpg', 'Image_76.jpg'] 

Number of training images for grapes : 100
Training samples for grapes : ['Image_84.jpg', 'Image_64.jpg', 'Image_1.jpg', 'Image_3.jpg', 'Image_76.jpg'] 

Number of training images for corn : 87
Training samples for corn : ['Image_84.jpg', 'Image_1.jpg', 'Image_40.jpg', 'Image_3.jpg', 'Image_7.jpg'] 

Number of training images for cauliflower : 79
Training samples for cauliflower : ['Image_1.jpg', 'Image_40.jpg', 'Image_3.jpg', 'Image_76.jpg', 'Image_7.jpg'] 

Number of training images for banana : 75
Training samples for banana : ['Image_84.jpg', 'Image_64.jpg', 'Image_1.jpg', 'Image_40.jpg', 'Image_3.jpg'] 

In [19]:
# Brief listing of test image files for each class
for c_label in CLASS_LABELS:
    test_class_dir = os.path.join(VALID_DIR, c_label)
    test_class_files = os.listdir(test_class_dir)
    print('Number of test images for', c_label, ':', len(os.listdir(test_class_dir)))
    print('Training samples for', c_label, ':')
    print(test_class_files[:5],'\n')
Number of test images for onion : 10
Training samples for onion :
['Image_1.jpg', 'Image_3.jpg', 'Image_8.jpg', 'Image_5.jpg', 'Image_6.jpg'] 

Number of test images for tomato : 10
Training samples for tomato :
['Image_1.jpg', 'Image_3.jpg', 'Image_7.jpg', 'Image_8.jpg', 'Image_5.jpg'] 

Number of test images for turnip : 10
Training samples for turnip :
['Image_1.jpg', 'Image_3.jpg', 'Image_7.jpg', 'Image_8.jpg', 'Image_5.jpg'] 

Number of test images for apple : 10
Training samples for apple :
['Image_1.jpg', 'Image_3.jpg', 'Image_7.jpg', 'Image_8.jpg', 'Image_5.jpg'] 

Number of test images for pomegranate : 10
Training samples for pomegranate :
['Image_1.jpg', 'Image_3.jpg', 'Image_7.jpg', 'Image_8.jpg', 'Image_5.jpg'] 

Number of test images for sweetcorn : 10
Training samples for sweetcorn :
['Image_1.jpg', 'Image_3.jpg', 'Image_7.jpg', 'Image_8.jpg', 'Image_5.jpg'] 

Number of test images for ginger : 10
Training samples for ginger :
['Image_1.jpg', 'Image_3.jpg', 'Image_7.jpg', 'Image_8.jpg', 'Image_5.jpg'] 

Number of test images for peas : 10
Training samples for peas :
['Image_1.jpg', 'Image_3.jpg', 'Image_7.jpg', 'Image_8.jpg', 'Image_5.jpg'] 

Number of test images for lettuce : 9
Training samples for lettuce :
['Image_1.jpg', 'Image_3.jpg', 'Image_7.jpg', 'Image_8.jpg', 'Image_6.jpg'] 

Number of test images for garlic : 10
Training samples for garlic :
['Image_1.jpg', 'Image_3.jpg', 'Image_7.jpg', 'Image_8.jpg', 'Image_5.jpg'] 

Number of test images for watermelon : 10
Training samples for watermelon :
['Image_1.jpg', 'Image_3.jpg', 'Image_7.jpg', 'Image_8.jpg', 'Image_5.jpg'] 

Number of test images for potato : 10
Training samples for potato :
['Image_1.jpg', 'Image_3.jpg', 'Image_7.jpg', 'Image_8.jpg', 'Image_6.jpg'] 

Number of test images for paprika : 10
Training samples for paprika :
['Image_1.jpg', 'Image_3.jpg', 'Image_7.jpg', 'Image_8.jpg', 'Image_5.jpg'] 

Number of test images for eggplant : 10
Training samples for eggplant :
['Image_1.jpg', 'Image_3.jpg', 'Image_7.jpg', 'Image_8.jpg', 'Image_5.jpg'] 

Number of test images for carrot : 9
Training samples for carrot :
['Image_1.jpg', 'Image_3.jpg', 'Image_8.jpg', 'Image_5.jpg', 'Image_6.jpg'] 

Number of test images for bell pepper : 9
Training samples for bell pepper :
['Image_1.jpg', 'Image_7.jpg', 'Image_8.jpg', 'Image_5.jpg', 'Image_6.jpg'] 

Number of test images for sweetpotato : 10
Training samples for sweetpotato :
['Image_1.jpg', 'Image_3.jpg', 'Image_7.jpg', 'Image_8.jpg', 'Image_5.jpg'] 

Number of test images for jalepeno : 9
Training samples for jalepeno :
['Image_3.jpg', 'Image_7.jpg', 'Image_8.jpg', 'Image_5.jpg', 'Image_6.jpg'] 

Number of test images for orange : 9
Training samples for orange :
['Image_3.jpg', 'Image_7.jpg', 'Image_5.jpg', 'Image_6.jpg', 'Image_9.jpg'] 

Number of test images for pineapple : 10
Training samples for pineapple :
['Image_1.jpg', 'Image_3.jpg', 'Image_7.jpg', 'Image_8.jpg', 'Image_5.jpg'] 

Number of test images for soy beans : 10
Training samples for soy beans :
['Image_1.jpg', 'Image_3.jpg', 'Image_7.jpg', 'Image_8.jpg', 'Image_5.jpg'] 

Number of test images for lemon : 10
Training samples for lemon :
['Image_3.jpg', 'Image_7.jpg', 'Image_8.jpg', 'Image_5.jpg', 'Image_10.jpg'] 

Number of test images for kiwi : 10
Training samples for kiwi :
['Image_1.jpg', 'Image_3.jpg', 'Image_7.jpg', 'Image_8.jpg', 'Image_5.jpg'] 

Number of test images for chilli pepper : 9
Training samples for chilli pepper :
['Image_1.jpg', 'Image_3.jpg', 'Image_7.jpg', 'Image_8.jpg', 'Image_10.jpg'] 

Number of test images for cucumber : 10
Training samples for cucumber :
['Image_1.jpg', 'Image_3.jpg', 'Image_7.jpg', 'Image_8.jpg', 'Image_5.jpg'] 

Number of test images for raddish : 9
Training samples for raddish :
['Image_1.jpg', 'Image_3.jpg', 'Image_7.jpg', 'Image_8.jpg', 'Image_6.jpg'] 

Number of test images for cabbage : 10
Training samples for cabbage :
['Image_1.jpg', 'Image_3.jpg', 'Image_7.jpg', 'Image_8.jpg', 'Image_5.jpg'] 

Number of test images for spinach : 10
Training samples for spinach :
['Image_1.jpg', 'Image_3.jpg', 'Image_7.jpg', 'Image_8.jpg', 'Image_5.jpg'] 

Number of test images for mango : 10
Training samples for mango :
['Image_1.jpg', 'Image_3.jpg', 'Image_7.jpg', 'Image_8.jpg', 'Image_5.jpg'] 

Number of test images for pear : 10
Training samples for pear :
['Image_1.jpg', 'Image_3.jpg', 'Image_7.jpg', 'Image_8.jpg', 'Image_5.jpg'] 

Number of test images for capsicum : 10
Training samples for capsicum :
['Image_1.jpg', 'Image_7.jpg', 'Image_3.JPG', 'Image_8.jpg', 'Image_5.jpg'] 

Number of test images for beetroot : 10
Training samples for beetroot :
['Image_1.jpg', 'Image_3.jpg', 'Image_7.jpg', 'Image_8.jpg', 'Image_5.jpg'] 

Number of test images for grapes : 9
Training samples for grapes :
['Image_1.jpg', 'Image_3.jpg', 'Image_8.jpg', 'Image_6.jpg', 'Image_10.jpg'] 

Number of test images for corn : 10
Training samples for corn :
['Image_1.jpg', 'Image_3.jpg', 'Image_7.jpg', 'Image_8.jpg', 'Image_5.jpg'] 

Number of test images for cauliflower : 10
Training samples for cauliflower :
['Image_1.jpg', 'Image_3.jpg', 'Image_7.jpg', 'Image_8.jpg', 'Image_6.jpg'] 

Number of test images for banana : 9
Training samples for banana :
['Image_1.jpg', 'Image_3.jpg', 'Image_8.jpg', 'Image_5.jpg', 'Image_6.jpg'] 

In [20]:
# Plot some training images from the dataset
nrows = len(CLASS_LABELS)
ncols = 4
training_examples = []
example_labels = []

fig = plt.gcf()
fig.set_size_inches(ncols * 4, nrows * 3)

for c_label in CLASS_LABELS:
    training_class_dir = os.path.join(TRAIN_DIR, c_label)
    training_class_files = os.listdir(training_class_dir)
    for j in range(ncols):
        training_examples.append(training_class_dir + '/' + training_class_files[j])
        example_labels.append(c_label)
    # print(training_examples)
    # print(example_labels)

for i, img_path in enumerate(training_examples):
    # Set up subplot; subplot indices start at 1
    sp = plt.subplot(nrows, ncols, i+1)
    sp.text(0, 0, example_labels[i])
    # sp.axis('Off')
    img = mpimg.imread(img_path)
    plt.imshow(img)
plt.show()
In [21]:
datagen_kwargs = dict(rescale=1./255)
training_datagen = ImageDataGenerator(**datagen_kwargs)
validation_datagen = ImageDataGenerator(**datagen_kwargs)
dataflow_kwargs = dict(class_mode="categorical")

do_data_augmentation = True
if do_data_augmentation:
    training_datagen = ImageDataGenerator(rotation_range=45,
                                          horizontal_flip=True,
                                          vertical_flip=True,
                                          **datagen_kwargs)

print('Loading and pre-processing the training images...')
training_generator = training_datagen.flow_from_directory(directory=TRAIN_DIR,
                                                          target_size=TARGET_IMAGE_SIZE,
                                                          batch_size=BATCH_SIZE,
                                                          shuffle=True,
                                                          seed=RNG_SEED,
                                                          **dataflow_kwargs)
print('Number of training image batches per epoch of modeling:', len(training_generator))

print('Loading and pre-processing the validation images...')
validation_generator = validation_datagen.flow_from_directory(directory=VALID_DIR,
                                                              target_size=TARGET_IMAGE_SIZE,
                                                              batch_size=BATCH_SIZE,
                                                              shuffle=False,
                                                              **dataflow_kwargs)
print('Number of validation image batches per epoch of modeling:', len(validation_generator))
Loading and pre-processing the training images...
Found 3115 images belonging to 36 classes.
Number of training image batches per epoch of modeling: 195
Loading and pre-processing the validation images...
Found 351 images belonging to 36 classes.
Number of validation image batches per epoch of modeling: 22
In [22]:
if NOTIFY_STATUS: status_notify('(TensorFlow Multi-Class) Task 2 - Load and Prepare Images completed on ' + datetime.now().strftime('%A %B %d, %Y %I:%M:%S %p'))

Task 3 - Define and Train Models¶

In [23]:
if NOTIFY_STATUS: status_notify('(TensorFlow Multi-Class) Task 3 - Define and Train Models has begun on ' + datetime.now().strftime('%A %B %d, %Y %I:%M:%S %p'))
In [24]:
# Define the function for plotting training results for comparison
def plot_metrics(history):
    fig, axs = plt.subplots(1, 2, figsize=(24, 15))
    metrics =  [TRAIN_LOSS, TRAIN_METRIC]
    for n, metric in enumerate(metrics):
        name = metric.replace("_"," ").capitalize()
        plt.subplot(2,2,n+1)
        plt.plot(history.epoch, history.history[metric], color='blue', label='Train')
        plt.plot(history.epoch, history.history['val_'+metric], color='red', linestyle="--", label='Val')
        plt.xlabel('Epoch')
        plt.ylabel(name)
        if metric == TRAIN_LOSS:
            plt.ylim([0, plt.ylim()[1]])
        else:
            plt.ylim([0, 1])
        plt.legend()
In [25]:
# Define the baseline model for benchmarking
def create_nn_model(input_param=INPUT_IMAGE_SHAPE, output_param=NUM_CLASSES, dense_nodes=2048,
                    classifier_activation=CLASSIFIER_ACTIVATION, loss_param=DEFAULT_LOSS,
                    opt_param=DEFAULT_OPTIMIZER, metrics_param=DEFAULT_METRICS):
    base_model = keras.applications.inception_v3.InceptionV3(include_top=False, weights='imagenet', input_shape=input_param)
    nn_model = keras.models.Sequential()
    nn_model.add(base_model)
    nn_model.add(keras.layers.Flatten())
    nn_model.add(keras.layers.Dense(dense_nodes, activation='relu')),
    nn_model.add(keras.layers.Dense(output_param, activation=classifier_activation))
    nn_model.compile(loss=loss_param, optimizer=opt_param, metrics=metrics_param)
    return nn_model
In [26]:
# Initialize the neural network model and get the training results for plotting graph
start_time_module = datetime.now()
tf.keras.utils.set_random_seed(RNG_SEED)
baseline_model = create_nn_model()
baseline_model_history = baseline_model.fit(training_generator,
                                            epochs=MAX_EPOCHS,
                                            validation_data=validation_generator,
                                            verbose=1)
print('Total time for model fitting:', (datetime.now() - start_time_module))
Downloading data from https://storage.googleapis.com/tensorflow/keras-applications/inception_v3/inception_v3_weights_tf_dim_ordering_tf_kernels_notop.h5
87916544/87910968 [==============================] - 1s 0us/step
87924736/87910968 [==============================] - 1s 0us/step
Epoch 1/20
 10/195 [>.............................] - ETA: 2:10 - loss: 11.7945 - accuracy: 0.0625
/usr/local/lib/python3.7/dist-packages/PIL/TiffImagePlugin.py:788: UserWarning: Corrupt EXIF data.  Expecting to read 4 bytes but only got 0. 
  warnings.warn(str(msg))
 29/195 [===>..........................] - ETA: 2:22 - loss: 7.9680 - accuracy: 0.1089
/usr/local/lib/python3.7/dist-packages/PIL/Image.py:960: UserWarning: Palette images with Transparency expressed in bytes should be converted to RGBA images
  "Palette images with Transparency expressed in bytes should be "
195/195 [==============================] - 214s 1s/step - loss: 2.7517 - accuracy: 0.4555 - val_loss: 0.5878 - val_accuracy: 0.8120
Epoch 2/20
195/195 [==============================] - 193s 987ms/step - loss: 0.9184 - accuracy: 0.7287 - val_loss: 0.2316 - val_accuracy: 0.9202
Epoch 3/20
195/195 [==============================] - 192s 988ms/step - loss: 0.6936 - accuracy: 0.7852 - val_loss: 0.2952 - val_accuracy: 0.8946
Epoch 4/20
195/195 [==============================] - 194s 996ms/step - loss: 0.5520 - accuracy: 0.8231 - val_loss: 0.4437 - val_accuracy: 0.8718
Epoch 5/20
195/195 [==============================] - 195s 999ms/step - loss: 0.4518 - accuracy: 0.8514 - val_loss: 0.2462 - val_accuracy: 0.9174
Epoch 6/20
195/195 [==============================] - 195s 998ms/step - loss: 0.4215 - accuracy: 0.8613 - val_loss: 0.2617 - val_accuracy: 0.9231
Epoch 7/20
195/195 [==============================] - 194s 996ms/step - loss: 0.3967 - accuracy: 0.8713 - val_loss: 0.2396 - val_accuracy: 0.9316
Epoch 8/20
195/195 [==============================] - 194s 996ms/step - loss: 0.3453 - accuracy: 0.8867 - val_loss: 0.2714 - val_accuracy: 0.9145
Epoch 9/20
195/195 [==============================] - 193s 984ms/step - loss: 0.3248 - accuracy: 0.8915 - val_loss: 0.2525 - val_accuracy: 0.9373
Epoch 10/20
195/195 [==============================] - 194s 992ms/step - loss: 0.2608 - accuracy: 0.9124 - val_loss: 0.2286 - val_accuracy: 0.9316
Epoch 11/20
195/195 [==============================] - 194s 992ms/step - loss: 0.2745 - accuracy: 0.9085 - val_loss: 0.2809 - val_accuracy: 0.9145
Epoch 12/20
195/195 [==============================] - 193s 989ms/step - loss: 0.2459 - accuracy: 0.9178 - val_loss: 0.1409 - val_accuracy: 0.9487
Epoch 13/20
195/195 [==============================] - 197s 1s/step - loss: 0.2983 - accuracy: 0.9095 - val_loss: 0.1956 - val_accuracy: 0.9573
Epoch 14/20
195/195 [==============================] - 197s 1s/step - loss: 0.2367 - accuracy: 0.9162 - val_loss: 0.1624 - val_accuracy: 0.9430
Epoch 15/20
195/195 [==============================] - 202s 1s/step - loss: 0.2172 - accuracy: 0.9242 - val_loss: 0.1868 - val_accuracy: 0.9516
Epoch 16/20
195/195 [==============================] - 196s 1s/step - loss: 0.2422 - accuracy: 0.9223 - val_loss: 0.1265 - val_accuracy: 0.9687
Epoch 17/20
195/195 [==============================] - 194s 995ms/step - loss: 0.1789 - accuracy: 0.9368 - val_loss: 0.1538 - val_accuracy: 0.9516
Epoch 18/20
195/195 [==============================] - 194s 993ms/step - loss: 0.2100 - accuracy: 0.9342 - val_loss: 0.2012 - val_accuracy: 0.9402
Epoch 19/20
195/195 [==============================] - 195s 1s/step - loss: 0.1757 - accuracy: 0.9429 - val_loss: 0.1432 - val_accuracy: 0.9544
Epoch 20/20
195/195 [==============================] - 195s 1s/step - loss: 0.2023 - accuracy: 0.9352 - val_loss: 0.2630 - val_accuracy: 0.9345
Total time for model fitting: 1:05:29.730418
In [27]:
baseline_model.summary()
Model: "sequential"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 inception_v3 (Functional)   (None, 8, 8, 2048)        21802784  
                                                                 
 flatten (Flatten)           (None, 131072)            0         
                                                                 
 dense (Dense)               (None, 2048)              268437504 
                                                                 
 dense_1 (Dense)             (None, 36)                73764     
                                                                 
=================================================================
Total params: 290,314,052
Trainable params: 290,279,620
Non-trainable params: 34,432
_________________________________________________________________
In [28]:
plot_metrics(baseline_model_history)
In [29]:
if NOTIFY_STATUS: status_notify('(TensorFlow Multi-Class) Task 3 - Define and Train Models completed on ' + datetime.now().strftime('%A %B %d, %Y %I:%M:%S %p'))

Task 4 - Tune and Optimize Models¶

In [30]:
if NOTIFY_STATUS: status_notify('(TensorFlow Multi-Class) Task 4 - Tune and Optimize Models has begun on ' + datetime.now().strftime('%A %B %d, %Y %I:%M:%S %p'))
In [31]:
# Initialize the neural network model and get the training results for plotting graph
start_time_module = datetime.now()
TUNING_LR = INITIAL_LR / 2
TUNE_OPTIMIZER = tf.keras.optimizers.Adam(learning_rate=TUNING_LR)
MINIMUM_LR = TUNING_LR / 4
learning_rate_reduction = ReduceLROnPlateau(monitor='val_accuracy', patience=3, verbose=1, factor=0.5, min_lr=MINIMUM_LR)
tf.keras.utils.set_random_seed(RNG_SEED)
tune_model = create_nn_model(opt_param=TUNE_OPTIMIZER)
tune_model_history = tune_model.fit(training_generator,
                                    epochs=MAX_EPOCHS,
                                    validation_data=validation_generator,
                                    callbacks=[learning_rate_reduction],
                                    verbose=1)
print('Total time for model fitting:', (datetime.now() - start_time_module))
/usr/local/lib/python3.7/dist-packages/PIL/Image.py:960: UserWarning: Palette images with Transparency expressed in bytes should be converted to RGBA images
  "Palette images with Transparency expressed in bytes should be "
Epoch 1/20
 36/195 [====>.........................] - ETA: 2:09 - loss: 5.0687 - accuracy: 0.1944
/usr/local/lib/python3.7/dist-packages/PIL/TiffImagePlugin.py:788: UserWarning: Corrupt EXIF data.  Expecting to read 4 bytes but only got 0. 
  warnings.warn(str(msg))
195/195 [==============================] - 205s 1s/step - loss: 2.2812 - accuracy: 0.4841 - val_loss: 0.5013 - val_accuracy: 0.8291 - lr: 5.0000e-05
Epoch 2/20
195/195 [==============================] - 195s 1s/step - loss: 0.8603 - accuracy: 0.7419 - val_loss: 0.2669 - val_accuracy: 0.9003 - lr: 5.0000e-05
Epoch 3/20
195/195 [==============================] - 195s 999ms/step - loss: 0.6277 - accuracy: 0.8039 - val_loss: 0.2369 - val_accuracy: 0.9003 - lr: 5.0000e-05
Epoch 4/20
195/195 [==============================] - 195s 996ms/step - loss: 0.4994 - accuracy: 0.8404 - val_loss: 0.2257 - val_accuracy: 0.9373 - lr: 5.0000e-05
Epoch 5/20
195/195 [==============================] - 193s 989ms/step - loss: 0.4388 - accuracy: 0.8639 - val_loss: 0.2406 - val_accuracy: 0.9259 - lr: 5.0000e-05
Epoch 6/20
195/195 [==============================] - 192s 982ms/step - loss: 0.3740 - accuracy: 0.8787 - val_loss: 0.1482 - val_accuracy: 0.9459 - lr: 5.0000e-05
Epoch 7/20
195/195 [==============================] - 193s 986ms/step - loss: 0.3111 - accuracy: 0.8934 - val_loss: 0.1830 - val_accuracy: 0.9487 - lr: 5.0000e-05
Epoch 8/20
195/195 [==============================] - 194s 993ms/step - loss: 0.3038 - accuracy: 0.9034 - val_loss: 0.1545 - val_accuracy: 0.9573 - lr: 5.0000e-05
Epoch 9/20
195/195 [==============================] - 195s 1000ms/step - loss: 0.2667 - accuracy: 0.9165 - val_loss: 0.1549 - val_accuracy: 0.9544 - lr: 5.0000e-05
Epoch 10/20
195/195 [==============================] - 194s 993ms/step - loss: 0.2409 - accuracy: 0.9246 - val_loss: 0.1306 - val_accuracy: 0.9658 - lr: 5.0000e-05
Epoch 11/20
195/195 [==============================] - 194s 997ms/step - loss: 0.2218 - accuracy: 0.9287 - val_loss: 0.1589 - val_accuracy: 0.9516 - lr: 5.0000e-05
Epoch 12/20
195/195 [==============================] - 192s 986ms/step - loss: 0.2028 - accuracy: 0.9368 - val_loss: 0.1915 - val_accuracy: 0.9516 - lr: 5.0000e-05
Epoch 13/20
195/195 [==============================] - ETA: 0s - loss: 0.2142 - accuracy: 0.9332
Epoch 13: ReduceLROnPlateau reducing learning rate to 2.499999936844688e-05.
195/195 [==============================] - 191s 979ms/step - loss: 0.2142 - accuracy: 0.9332 - val_loss: 0.1490 - val_accuracy: 0.9573 - lr: 5.0000e-05
Epoch 14/20
195/195 [==============================] - 192s 984ms/step - loss: 0.1328 - accuracy: 0.9596 - val_loss: 0.1207 - val_accuracy: 0.9601 - lr: 2.5000e-05
Epoch 15/20
195/195 [==============================] - 193s 989ms/step - loss: 0.0941 - accuracy: 0.9653 - val_loss: 0.1381 - val_accuracy: 0.9630 - lr: 2.5000e-05
Epoch 16/20
195/195 [==============================] - 195s 1s/step - loss: 0.0685 - accuracy: 0.9737 - val_loss: 0.1244 - val_accuracy: 0.9715 - lr: 2.5000e-05
Epoch 17/20
195/195 [==============================] - 196s 1s/step - loss: 0.0910 - accuracy: 0.9717 - val_loss: 0.1381 - val_accuracy: 0.9715 - lr: 2.5000e-05
Epoch 18/20
195/195 [==============================] - 200s 1s/step - loss: 0.0724 - accuracy: 0.9775 - val_loss: 0.1291 - val_accuracy: 0.9715 - lr: 2.5000e-05
Epoch 19/20
195/195 [==============================] - ETA: 0s - loss: 0.0929 - accuracy: 0.9685
Epoch 19: ReduceLROnPlateau reducing learning rate to 1.25e-05.
195/195 [==============================] - 195s 996ms/step - loss: 0.0929 - accuracy: 0.9685 - val_loss: 0.1389 - val_accuracy: 0.9687 - lr: 2.5000e-05
Epoch 20/20
195/195 [==============================] - 192s 986ms/step - loss: 0.0682 - accuracy: 0.9804 - val_loss: 0.1177 - val_accuracy: 0.9687 - lr: 1.2500e-05
Total time for model fitting: 1:05:02.781417
In [32]:
if NOTIFY_STATUS: status_notify('(TensorFlow Multi-Class) Task 4 - Tune and Optimize Models completed on ' + datetime.now().strftime('%A %B %d, %Y %I:%M:%S %p'))

Task 5 - Finalize Model and Make Predictions¶

In [33]:
if NOTIFY_STATUS: status_notify('(TensorFlow Multi-Class) Task 5 - Finalize Model and Make Predictions has begun on ' + datetime.now().strftime('%A %B %d, %Y %I:%M:%S %p'))

5.a) Train the Final Model¶

In [34]:
FINAL_LR = 0.0000125
FINAL_OPTIMIZER = tf.keras.optimizers.Adam(learning_rate=FINAL_LR)
FINAL_EPOCHS = MAX_EPOCHS
tf.keras.utils.set_random_seed(RNG_SEED)
final_model = create_nn_model(opt_param=FINAL_OPTIMIZER)
final_model.fit(training_generator, epochs=FINAL_EPOCHS, verbose=1)
final_model.summary()
Epoch 1/20
 19/195 [=>............................] - ETA: 2:45 - loss: 3.9055 - accuracy: 0.1438
/usr/local/lib/python3.7/dist-packages/PIL/TiffImagePlugin.py:788: UserWarning: Corrupt EXIF data.  Expecting to read 4 bytes but only got 0. 
  warnings.warn(str(msg))
 87/195 [============>.................] - ETA: 1:39 - loss: 2.8374 - accuracy: 0.2985
/usr/local/lib/python3.7/dist-packages/PIL/Image.py:960: UserWarning: Palette images with Transparency expressed in bytes should be converted to RGBA images
  "Palette images with Transparency expressed in bytes should be "
195/195 [==============================] - 182s 898ms/step - loss: 2.1186 - accuracy: 0.4453
Epoch 2/20
195/195 [==============================] - 177s 910ms/step - loss: 1.0572 - accuracy: 0.6944
Epoch 3/20
195/195 [==============================] - 177s 907ms/step - loss: 0.7829 - accuracy: 0.7705
Epoch 4/20
195/195 [==============================] - 178s 909ms/step - loss: 0.5864 - accuracy: 0.8209
Epoch 5/20
195/195 [==============================] - 178s 913ms/step - loss: 0.5425 - accuracy: 0.8283
Epoch 6/20
195/195 [==============================] - 178s 913ms/step - loss: 0.4361 - accuracy: 0.8575
Epoch 7/20
195/195 [==============================] - 178s 910ms/step - loss: 0.4027 - accuracy: 0.8661
Epoch 8/20
195/195 [==============================] - 177s 909ms/step - loss: 0.3096 - accuracy: 0.8970
Epoch 9/20
195/195 [==============================] - 176s 905ms/step - loss: 0.3096 - accuracy: 0.8973
Epoch 10/20
195/195 [==============================] - 176s 904ms/step - loss: 0.2818 - accuracy: 0.9088
Epoch 11/20
195/195 [==============================] - 176s 901ms/step - loss: 0.2537 - accuracy: 0.9101
Epoch 12/20
195/195 [==============================] - 176s 905ms/step - loss: 0.2610 - accuracy: 0.9175
Epoch 13/20
195/195 [==============================] - 175s 897ms/step - loss: 0.1965 - accuracy: 0.9352
Epoch 14/20
195/195 [==============================] - 174s 892ms/step - loss: 0.1955 - accuracy: 0.9355
Epoch 15/20
195/195 [==============================] - 176s 901ms/step - loss: 0.1527 - accuracy: 0.9502
Epoch 16/20
195/195 [==============================] - 177s 906ms/step - loss: 0.1872 - accuracy: 0.9374
Epoch 17/20
195/195 [==============================] - 178s 910ms/step - loss: 0.1626 - accuracy: 0.9403
Epoch 18/20
195/195 [==============================] - 175s 899ms/step - loss: 0.1461 - accuracy: 0.9522
Epoch 19/20
195/195 [==============================] - 177s 906ms/step - loss: 0.1769 - accuracy: 0.9390
Epoch 20/20
195/195 [==============================] - 178s 913ms/step - loss: 0.1099 - accuracy: 0.9640
Model: "sequential_2"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 inception_v3 (Functional)   (None, 8, 8, 2048)        21802784  
                                                                 
 flatten_2 (Flatten)         (None, 131072)            0         
                                                                 
 dense_4 (Dense)             (None, 2048)              268437504 
                                                                 
 dense_5 (Dense)             (None, 36)                73764     
                                                                 
=================================================================
Total params: 290,314,052
Trainable params: 290,279,620
Non-trainable params: 34,432
_________________________________________________________________

5.b) Load Test Dataset and Make Predictions¶

In [35]:
# Brief listing of test image files for each class
for c_label in CLASS_LABELS:
    test_class_dir = os.path.join(TEST_DIR, c_label)
    test_class_files = os.listdir(test_class_dir)
    print('Number of test images for', c_label, ':', len(os.listdir(test_class_dir)))
    print('Training samples for', c_label, ':')
    print(test_class_files[:5],'\n')
Number of test images for onion : 10
Training samples for onion :
['Image_1.jpg', 'Image_3.jpg', 'Image_8.jpg', 'Image_5.jpg', 'Image_6.jpg'] 

Number of test images for tomato : 10
Training samples for tomato :
['Image_1.jpg', 'Image_3.jpg', 'Image_7.jpg', 'Image_8.jpg', 'Image_5.jpg'] 

Number of test images for turnip : 10
Training samples for turnip :
['Image_1.jpg', 'Image_3.jpg', 'Image_7.jpg', 'Image_8.jpg', 'Image_5.jpg'] 

Number of test images for apple : 10
Training samples for apple :
['Image_1.jpg', 'Image_3.jpg', 'Image_7.jpg', 'Image_8.jpg', 'Image_5.jpg'] 

Number of test images for pomegranate : 10
Training samples for pomegranate :
['Image_1.jpg', 'Image_3.jpg', 'Image_7.jpg', 'Image_8.jpg', 'Image_5.jpg'] 

Number of test images for sweetcorn : 10
Training samples for sweetcorn :
['Image_1.jpg', 'Image_3.jpg', 'Image_7.jpg', 'Image_8.jpg', 'Image_5.jpg'] 

Number of test images for ginger : 10
Training samples for ginger :
['Image_1.jpg', 'Image_3.jpg', 'Image_7.jpg', 'Image_8.jpg', 'Image_5.jpg'] 

Number of test images for peas : 10
Training samples for peas :
['Image_1.jpg', 'Image_3.jpg', 'Image_7.jpg', 'Image_8.jpg', 'Image_5.jpg'] 

Number of test images for lettuce : 10
Training samples for lettuce :
['Image_1.jpg', 'Image_3.jpg', 'Image_7.jpg', 'Image_8.jpg', 'Image_6.jpg'] 

Number of test images for garlic : 10
Training samples for garlic :
['Image_1.jpg', 'Image_3.jpg', 'Image_7.jpg', 'Image_8.jpg', 'Image_5.jpg'] 

Number of test images for watermelon : 10
Training samples for watermelon :
['Image_1.jpg', 'Image_3.jpg', 'Image_7.jpg', 'Image_8.jpg', 'Image_5.jpg'] 

Number of test images for potato : 10
Training samples for potato :
['Image_1.jpg', 'Image_3.jpg', 'Image_7.jpg', 'Image_8.jpg', 'Image_6.jpg'] 

Number of test images for paprika : 10
Training samples for paprika :
['Image_1.jpg', 'Image_3.jpg', 'Image_7.jpg', 'Image_8.jpg', 'Image_5.jpg'] 

Number of test images for eggplant : 10
Training samples for eggplant :
['Image_1.jpg', 'Image_3.jpg', 'Image_7.jpg', 'Image_8.jpg', 'Image_5.jpg'] 

Number of test images for carrot : 10
Training samples for carrot :
['Image_1.jpg', 'Image_3.jpg', 'Image_8.jpg', 'Image_5.jpg', 'Image_6.jpg'] 

Number of test images for bell pepper : 10
Training samples for bell pepper :
['Image_1.jpg', 'Image_7.jpg', 'Image_8.jpg', 'Image_5.jpg', 'Image_6.jpg'] 

Number of test images for sweetpotato : 10
Training samples for sweetpotato :
['Image_1.jpg', 'Image_3.jpg', 'Image_7.jpg', 'Image_8.jpg', 'Image_5.jpg'] 

Number of test images for jalepeno : 10
Training samples for jalepeno :
['Image_3.jpg', 'Image_7.jpg', 'Image_8.jpg', 'Image_5.jpg', 'Image_6.jpg'] 

Number of test images for orange : 10
Training samples for orange :
['Image_3.jpg', 'Image_7.jpg', 'Image_8.jpeg', 'Image_5.jpg', 'Image_6.jpg'] 

Number of test images for pineapple : 10
Training samples for pineapple :
['Image_1.jpg', 'Image_3.jpg', 'Image_7.jpg', 'Image_8.jpg', 'Image_5.jpg'] 

Number of test images for soy beans : 10
Training samples for soy beans :
['Image_1.jpg', 'Image_3.jpg', 'Image_7.jpg', 'Image_8.jpg', 'Image_5.jpg'] 

Number of test images for lemon : 10
Training samples for lemon :
['Image_3.jpg', 'Image_7.jpg', 'Image_8.jpg', 'Image_5.jpg', 'Image_10.jpg'] 

Number of test images for kiwi : 10
Training samples for kiwi :
['Image_1.jpg', 'Image_3.jpg', 'Image_7.jpg', 'Image_8.jpg', 'Image_5.jpg'] 

Number of test images for chilli pepper : 10
Training samples for chilli pepper :
['Image_1.jpg', 'Image_3.jpg', 'Image_7.jpg', 'Image_8.jpg', 'Image_6.jpeg'] 

Number of test images for cucumber : 10
Training samples for cucumber :
['Image_1.jpg', 'Image_3.jpg', 'Image_7.jpg', 'Image_8.jpg', 'Image_5.jpg'] 

Number of test images for raddish : 10
Training samples for raddish :
['Image_1.jpg', 'Image_3.jpg', 'Image_7.jpg', 'Image_8.jpg', 'Image_6.jpg'] 

Number of test images for cabbage : 10
Training samples for cabbage :
['Image_1.jpg', 'Image_3.jpg', 'Image_7.jpg', 'Image_8.jpg', 'Image_5.jpg'] 

Number of test images for spinach : 10
Training samples for spinach :
['Image_1.jpg', 'Image_3.jpg', 'Image_7.jpg', 'Image_8.jpg', 'Image_5.jpg'] 

Number of test images for mango : 10
Training samples for mango :
['Image_1.jpg', 'Image_3.jpg', 'Image_7.jpg', 'Image_8.jpg', 'Image_5.jpg'] 

Number of test images for pear : 10
Training samples for pear :
['Image_1.jpg', 'Image_3.jpg', 'Image_7.jpg', 'Image_8.jpg', 'Image_5.jpg'] 

Number of test images for capsicum : 10
Training samples for capsicum :
['Image_1.jpg', 'Image_7.jpg', 'Image_3.JPG', 'Image_8.jpg', 'Image_5.jpg'] 

Number of test images for beetroot : 10
Training samples for beetroot :
['Image_1.jpg', 'Image_3.jpg', 'Image_7.jpg', 'Image_8.jpg', 'Image_5.jpg'] 

Number of test images for grapes : 10
Training samples for grapes :
['Image_1.jpg', 'Image_3.jpg', 'Image_8.jpg', 'Image_6.jpg', 'Image_10.jpg'] 

Number of test images for corn : 10
Training samples for corn :
['Image_1.jpg', 'Image_3.jpg', 'Image_7.jpg', 'Image_8.jpg', 'Image_5.jpg'] 

Number of test images for cauliflower : 10
Training samples for cauliflower :
['Image_1.jpg', 'Image_3.jpg', 'Image_7.jpg', 'Image_8.jpg', 'Image_6.jpg'] 

Number of test images for banana : 9
Training samples for banana :
['Image_1.jpg', 'Image_3.jpg', 'Image_8.jpg', 'Image_5.jpg', 'Image_6.jpg'] 

In [36]:
datagen_kwargs = dict(rescale=1./255)
test_datagen = ImageDataGenerator(**datagen_kwargs)
dataflow_kwargs = dict(class_mode="categorical")

print('Loading and pre-processing the test images...')
test_generator = validation_datagen.flow_from_directory(directory=TEST_DIR,
                                                        target_size=TARGET_IMAGE_SIZE,
                                                        batch_size=BATCH_SIZE,
                                                        shuffle=False,
                                                        **dataflow_kwargs)
print('Number of test image batches per epoch of modeling:', len(test_generator))
Loading and pre-processing the test images...
Found 359 images belonging to 36 classes.
Number of test image batches per epoch of modeling: 23
In [37]:
# Print the labels used for the modeling
print(test_generator.class_indices)
{'apple': 0, 'banana': 1, 'beetroot': 2, 'bell pepper': 3, 'cabbage': 4, 'capsicum': 5, 'carrot': 6, 'cauliflower': 7, 'chilli pepper': 8, 'corn': 9, 'cucumber': 10, 'eggplant': 11, 'garlic': 12, 'ginger': 13, 'grapes': 14, 'jalepeno': 15, 'kiwi': 16, 'lemon': 17, 'lettuce': 18, 'mango': 19, 'onion': 20, 'orange': 21, 'paprika': 22, 'pear': 23, 'peas': 24, 'pineapple': 25, 'pomegranate': 26, 'potato': 27, 'raddish': 28, 'soy beans': 29, 'spinach': 30, 'sweetcorn': 31, 'sweetpotato': 32, 'tomato': 33, 'turnip': 34, 'watermelon': 35}
In [38]:
final_model.evaluate(test_generator, verbose=1)
13/23 [===============>..............] - ETA: 9s - loss: 0.1535 - accuracy: 0.9519 
/usr/local/lib/python3.7/dist-packages/PIL/TiffImagePlugin.py:788: UserWarning: Corrupt EXIF data.  Expecting to read 4 bytes but only got 0. 
  warnings.warn(str(msg))
23/23 [==============================] - 21s 883ms/step - loss: 0.1747 - accuracy: 0.9554
Out[38]:
[0.17473603785037994, 0.9554317593574524]
In [39]:
test_pred = final_model.predict(test_generator)
test_predictions = np.argmax(test_pred, axis=-1)
test_original = test_generator.labels
print('Accuracy Score:', accuracy_score(test_original, test_predictions))
print(confusion_matrix(test_original, test_predictions))
print(classification_report(test_original, test_predictions))
/usr/local/lib/python3.7/dist-packages/PIL/TiffImagePlugin.py:788: UserWarning: Corrupt EXIF data.  Expecting to read 4 bytes but only got 0. 
  warnings.warn(str(msg))
Accuracy Score: 0.9554317548746518
[[ 8  1  0 ...  0  0  0]
 [ 0  7  0 ...  0  0  0]
 [ 0  0 10 ...  0  0  0]
 ...
 [ 0  0  0 ... 10  0  0]
 [ 0  0  0 ...  0 10  0]
 [ 0  0  0 ...  0  0 10]]
              precision    recall  f1-score   support

           0       0.89      0.80      0.84        10
           1       0.88      0.78      0.82         9
           2       1.00      1.00      1.00        10
           3       0.83      1.00      0.91        10
           4       1.00      1.00      1.00        10
           5       1.00      0.80      0.89        10
           6       1.00      0.90      0.95        10
           7       1.00      1.00      1.00        10
           8       0.91      1.00      0.95        10
           9       0.89      0.80      0.84        10
          10       1.00      1.00      1.00        10
          11       1.00      0.90      0.95        10
          12       1.00      1.00      1.00        10
          13       1.00      1.00      1.00        10
          14       1.00      1.00      1.00        10
          15       1.00      1.00      1.00        10
          16       1.00      1.00      1.00        10
          17       1.00      1.00      1.00        10
          18       1.00      1.00      1.00        10
          19       0.91      1.00      0.95        10
          20       1.00      1.00      1.00        10
          21       0.77      1.00      0.87        10
          22       1.00      1.00      1.00        10
          23       1.00      1.00      1.00        10
          24       1.00      1.00      1.00        10
          25       0.83      1.00      0.91        10
          26       1.00      1.00      1.00        10
          27       0.80      0.80      0.80        10
          28       1.00      1.00      1.00        10
          29       1.00      1.00      1.00        10
          30       1.00      1.00      1.00        10
          31       0.82      0.90      0.86        10
          32       1.00      0.70      0.82        10
          33       1.00      1.00      1.00        10
          34       1.00      1.00      1.00        10
          35       1.00      1.00      1.00        10

    accuracy                           0.96       359
   macro avg       0.96      0.95      0.95       359
weighted avg       0.96      0.96      0.95       359

In [40]:
if NOTIFY_STATUS: status_notify('(TensorFlow Multi-Class) Task 5 - Finalize Model and Make Predictions completed on ' + datetime.now().strftime('%A %B %d, %Y %I:%M:%S %p'))
In [41]:
print ('Total time for the script:',(datetime.now() - START_TIME_SCRIPT))
Total time for the script: 3:12:04.082269